package com.ccll.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ccll.constants.GlobalConstant;
import com.ccll.domain.common.BaseContextHolder;
import com.ccll.domain.dto.PageDto;
import com.ccll.domain.po.File;
import com.ccll.mapper.FileMapper;
import com.ccll.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Seon
 * @date 2022/11/12 20:45
 */
@Slf4j
@Service
@Transactional
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {


    //不是文件夹
    private final Integer IS_NOT_FOLDER = 0;
    @Value("${cloud-disk.base-path}")
    private String basePath;
    @Value("${cloud-disk.img-path}")
    private String imgPath;
    @Value("${cloud-disk.video-path}")
    private String videoPath;
    @Value("${cloud-disk.music-path}")
    private String musicPath;
    @Value("${cloud-disk.file-path}")
    private String fileTypePath;
    @Value("${cloud-disk.zip-path}")
    private String zipPath;
    @Value("${cloud-disk.other-path}")
    private String otherPath;
    @Value("${cloud-disk.separator}")
    private String separator;

    @Autowired
    private FileMapper fileMapper;

    @Override
    public Page<File> getFilePage(PageDto pageDto, Long userId) {
        Page<File> page = new Page<>();
        page.setCurrent(pageDto.getCurrentPage());
        page.setSize(pageDto.getPageSize());
        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(userId != null, File::getUserId, userId);
        this.page(page, wrapper);
        return page;
    }

    @Override
    public String uploadFile(MultipartFile file) {

        String originalName = file.getOriginalFilename();
        String uuid = UUID.randomUUID().toString();
        String suffix = originalName.substring(originalName.indexOf("."));
        String finalName = uuid + suffix;
        String protectPath;

        if (Arrays.asList(GlobalConstant.img).contains(suffix)) {
            protectPath = basePath + imgPath;
        } else if (Arrays.asList(GlobalConstant.video).contains(suffix)) {
            protectPath = basePath + videoPath;
        } else if (Arrays.asList(GlobalConstant.music).contains(suffix)) {
            protectPath = basePath + musicPath;
        } else if (Arrays.asList(GlobalConstant.file).contains(suffix)) {
            protectPath = basePath + fileTypePath;
        } else if (Arrays.asList(GlobalConstant.zip).contains(suffix)) {
            protectPath = basePath + zipPath;
        } else {
            protectPath = basePath + otherPath;
        }

        java.io.File dir = new java.io.File(protectPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        try {
            file.transferTo(new java.io.File(dir + separator + finalName));
            log.info("已上传文件" + dir + separator + finalName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return protectPath + separator + finalName;
    }

    @Override
    public List<File> getUserFolder(String initPath) {
        Long userId = BaseContextHolder.getCurrentId();
        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(File::getUserId, userId);
        wrapper.likeRight(File::getFilePath, initPath);
        wrapper.orderByDesc(File::getIsFolder);
        List<File> fileList = this.list(wrapper);
        List<File> userFolder = new ArrayList<>();
        for (File file : fileList) {
            String filePath = file.getFilePath();
            filePath = filePath.substring(filePath.indexOf(initPath) + initPath.length());
            if (!filePath.contains("/")) {
                file.setFilePath(filePath);
                userFolder.add(file);
            }
        }
        return userFolder;
    }

    @Override
    public Page<File> getFileByType(PageDto pageDto, String type) {
        Page<File> page = new Page<>();
        page.setSize(pageDto.getPageSize());
        page.setCurrent(pageDto.getCurrentPage());

        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(File::getFileType, type);
        wrapper.orderByDesc(File::getUpdateTime);
        this.page(page, wrapper);
        return page;
    }

    @Override
    public Map<String, Object> getFolderInformation(Long id) {
        Map<String, Object> map = new HashMap<>();
        File file = this.getById(id);
        String filePath = file.getFilePath();
        Long userId = BaseContextHolder.getCurrentId();

        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(File::getUserId, userId);
        wrapper.eq(File::getIsFolder, 0);
        wrapper.likeRight(File::getFilePath, filePath);

        List<File> fileList = this.list(wrapper);

        List<File> files = new LambdaQueryChainWrapper<>(fileMapper)
                .eq(File::getUserId, userId)
                .eq(File::getIsFolder, filePath)
                .likeRight(File::getFilePath, filePath)
                .list();
        List<File> files1 = this.list(
                Wrappers.lambdaQuery(File.class)
                        .eq(File::getUserId, userId)
                        .eq(File::getIsFolder, 0)
                        .like(File::getFilePath, filePath)
        );

        BigDecimal sumSize = BigDecimal.ZERO;
        for (File compute : fileList) {
            sumSize = sumSize.add(compute.getFileSize());
        }
        map.put("file", file);
        map.put("sumSize", sumSize);
        map.put("subfileNumber", fileList.size());
        return map;
    }

    @Override
    public List<File> searchFileCondition(String userId, String filename, String type) {
        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(File::getFileName, filename);
        wrapper.eq(File::getIsFolder, IS_NOT_FOLDER);
        wrapper.eq(userId != null, File::getUserId, userId);
        wrapper.eq(StringUtils.hasText(type), File::getFileType, type);
        wrapper.orderByDesc(File::getUpdateTime);
        List<File> list = this.list(wrapper);
        return list;
    }

    @Override
    public boolean deleteFolder(String folderPath) {
        Long userId = BaseContextHolder.getCurrentId();
        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(File::getUserId, userId);
        wrapper.likeRight(File::getFilePath, folderPath);
        boolean remove = this.remove(wrapper);
        return remove;
    }

    /**
     * 判断realPath是否还存在引用。
     *
     * @param judgePath
     * @return
     */
    @Override
    public boolean isRealPathReference(String judgePath) {
        Long userId = BaseContextHolder.getCurrentId();
        List<String> realPaths = fileMapper.getRealPathCondition(userId, 0);
        boolean result = realPaths.contains(judgePath);
        return result;
    }

    /**
     * 复制文件
     *
     * @param originPath
     * @param targetPath
     * @return
     */
    @Override
    public boolean copy(String originPath, String targetPath) {
        Long userId = BaseContextHolder.getCurrentId();
        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(File::getUserId, userId);
        wrapper.likeRight(File::getFilePath, originPath);

        List<File> list = this.list(wrapper);
        String truncatePath = originPath.substring(originPath.lastIndexOf("/"));

        List<File> handleFile = list.stream().map(items -> {
            File file = new File();
            BeanUtils.copyProperties(items, file, "id", "updateTime", "createTime");
            String itemPath = items.getFilePath();

            itemPath = itemPath.substring(itemPath.indexOf(originPath) + originPath.length());
            String newFilePath = targetPath + truncatePath + itemPath;
            file.setFilePath(newFilePath);
            return file;
        }).collect(Collectors.toList());
        boolean save = this.saveBatch(handleFile);
        return save;
    }

    @Override
    public boolean rename(String originName, String targetName) {
        try {
            LambdaQueryWrapper<File> fileWrapper = new LambdaQueryWrapper<>();
            fileWrapper.likeRight(File::getFilePath, originName);
            List<File> list = this.list(fileWrapper);
            list = list.stream().peek(file -> {
                String item = file.getFilePath();
                if (item.equals(originName)) {
                    String rename = targetName.substring(targetName.lastIndexOf("/") + 1);
                    file.setFileName(rename);
                }
                item = item.substring(item.indexOf(originName) + originName.length());
                String newPath = targetName + item;
                file.setFilePath(newPath);
            }).collect(Collectors.toList());
            this.updateBatchById(list);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean recoveryFile(Long id) {
        File file = fileMapper.physicalSearch(id);
        String filePath = file.getFilePath();
        String[] split = filePath.split("/");
        // j从1开始，因为split中第一个元素是空的。
        List<String> pathList = new ArrayList<>();
        for (int i = split.length - 1; i > 0; i --) {
            StringBuilder dealPath = new StringBuilder();
            for (int j = 1; j < i+1 ; j ++) {
                dealPath.append("/").append(split[j]);
            }
            pathList.add(dealPath.toString());
        }
        Map<String, Object> map = new HashMap<>();
        map.put("userId", BaseContextHolder.getCurrentId());
        for (String path : pathList) {
            map.put("filePath", path);
            boolean result = fileMapper.recoveryFileByPath(map);
            // 如果检测到上一级的文件夹已经被恢复了，那说明已经有子文件夹恢复过了，直接跳过就好了。
            if (!result) {
                break;
            }
        }
        return true;
    }

}
